#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <aura.h>
#include "private.h"
#include "module.h"

static DLinkNode *s_pAttachRefModuleList;


EXTERN_C void AddToAttachList(CModule *pModule)
{
    ModuleRef *pModRef;

    if(NULL == s_pAttachRefModuleList){
        s_pAttachRefModuleList = new DLinkNode();
        if(NULL != s_pAttachRefModuleList){
            s_pAttachRefModuleList->Initialize();
        }
    }

    pModRef = new ModuleRef(pModule);
    if (!pModRef) {
        Aura_printf("Failed to AddToAttachList modue:");
        Aura_putws(pModule->m_wszFullPath);
        Aura_printf(" \n");
        return;
    }

    s_pAttachRefModuleList->InsertPrev(pModRef);



}

EXTERN_C void DelFromAttachList(CModule *pModule)
{
    ModuleRef *pModRef;

    if(NULL == s_pAttachRefModuleList){
        return;
    }

//    Aura_printf(" DelFromAttachList modue: ");
//    Aura_putws(pModule->m_wszFullPath);
//    Aura_printf(" \n");

    ForEachDLinkNode(ModuleRef *, pModRef, s_pAttachRefModuleList) {
        if (pModRef->m_pModule == pModule) {
            pModRef->Detach();
            delete pModRef;
            return;
        }
    }
}


Aura_Bool CModule::AllocMemery(Aura_UInt32 uImageBase, Aura_UInt32 uTotalSize)
{
    Aura_Int32 flags;
    if(ModuleFileType_EXE == m_fileType){
        flags = MAP_FIXED;
    }else {
        flags = 0;
    }
    m_uImageBase = (Aura_UInt32)AnonymousMmap(
                                (Aura_PVoid)uImageBase,
                                uTotalSize,
                                PROT_READ|PROT_WRITE|PROT_EXEC,
                                flags);
    if(0 == m_uImageBase){
        PERROR("Error: Map memery to moudle fail! file:%s. line:%d.\n", __FILE__, __LINE__);
        return AURA_FALSE;
    }
    PTRACE("Alloc module image base:0x%x.\n", m_uImageBase);
    return AURA_TRUE;
}

typedef Aura_Bool __stdcall (*DLLMAINCRTSTARTUP)(Aura_PVoid hDllHandle, \
                            int dwReason, Aura_PVoid preserved);

Aura_Bool CModule::AttachProcess(Aura_PVoid preserved)
{
    DLLMAINCRTSTARTUP entry;
    Aura_Bool ret = AURA_TRUE;
    if(ModuleTachStatus_INIT != m_attached){
        return AURA_TRUE;
    }

    if(m_uEntryPoint && (ModuleFileType_DLL == m_fileType)){
        entry = (DLLMAINCRTSTARTUP)m_uEntryPoint;
        ret = entry((Aura_PVoid)m_uImageBase, DLL_PROCESS_ATTACH, preserved);
        if(!ret){
            ret =  AURA_FALSE;
        }
    }
    m_attached = ModuleTachStatus_ATTACHED;
    return ret;
}

Aura_Bool CModule::DetachProcess(Aura_PVoid preserved)
{
    DLLMAINCRTSTARTUP entry;
    Aura_Bool ret = AURA_TRUE;

    if(ModuleTachStatus_ATTACHED != m_attached){
        return AURA_TRUE;
    }

    if(m_uEntryPoint && (ModuleFileType_DLL == m_fileType)){
        entry = (DLLMAINCRTSTARTUP)m_uEntryPoint;
        if(!entry((Aura_PVoid)m_uImageBase, DLL_PROCESS_DETACH, preserved)){
            ret =  AURA_FALSE;
        }
    }
    m_attached = ModuleTachStatus_DETACHED;
    return ret;
}

Aura_UInt32 CModule::AddRef(void)
{
    Aura_Int32 lRef = m_cRef.Increment();
    return lRef;
}

Aura_UInt32 CModule::Release(void)
{
    Aura_Int32 lRef = m_cRef.Decrement();

    assert(lRef >= 0);
    if (0 == lRef) {
        START_LOCK(g_hAttachLock);
        DetachProcess(NULL);
        DelFromAttachList(this);
        END_LOCK(g_hAttachLock);
        GUnloadModule(this);
        delete this;
    }
    return (Aura_UInt32)lRef;
}

EXTERN_C Aura_Bool ProcessAttach(CModule *pModule, Aura_PVoid preserved)
{
    ModuleRef *pModRef;
    Aura_Bool ret = AURA_TRUE;
    ForEachDLinkNode(ModuleRef *, pModRef, s_pAttachRefModuleList) {
        ret = pModRef->m_pModule->AttachProcess(preserved);
        if(!ret){
            return ret;
        }
    }
    return ret;
}


#if 0

EXTERN_C void ProcessDetach(DLinkNode *pModuleList, Aura_PVoid preserved)
{
    CModule *pModule;

    delete s_pAttachRefModuleList;
    s_pAttachRefModuleList = NULL;

    ForEachDLinkNodeReversely(CModule *, pModule, pModuleList) {
        pModule->DetachProcess(preserved);
    }
}

Aura_Bool CModule::AttachThread(Aura_PVoid preserved)
{
    //Must be locked
    DLLMAINCRTSTARTUP entry;
    Aura_Bool ret = AURA_TRUE;

    if(ModuleTachStatus_ATTACHED != m_attached){
        return AURA_TRUE;
    }

    if(m_uEntryPoint && (ModuleFileType_DLL == m_fileType)){
        entry = (DLLMAINCRTSTARTUP)m_uEntryPoint;
        if(!entry((Aura_PVoid)m_uImageBase, DLL_THREAD_ATTACH, preserved)){
            ret =  AURA_FALSE;
        }
    }
    return ret;
}

Aura_Bool CModule::DetachThread(Aura_PVoid preserved)
{
    //Must be locked
    DLLMAINCRTSTARTUP entry;
    Aura_Bool ret = AURA_TRUE;

    if(ModuleTachStatus_ATTACHED != m_attached){
        return AURA_TRUE;
    }

    if(m_uEntryPoint && (ModuleFileType_DLL == m_fileType)){
        entry = (DLLMAINCRTSTARTUP)m_uEntryPoint;
        if(!entry((Aura_PVoid)m_uImageBase, DLL_THREAD_DETACH, preserved)){
            ret =  AURA_FALSE;
        }
    }
    return ret;
}

EXTERN_C Aura_Bool ThreadAttach(CModule *pModule, Aura_PVoid preserved)
{
    ModuleRef *pModRef;
    Aura_Bool ret = AURA_TRUE;
    //tachLock

    ForEachDLinkNode(ModuleRef *, pModRef, &(pModule->m_rModuleList)) {
        ret = ThreadAttach(pModRef->m_pModule, preserved);
        if(!ret) return ret;
    }
    ret = pModule->AttachThread(preserved);
    return ret;
}

EXTERN_C Aura_Bool ThreadDetach(DLinkNode *pModuleList, Aura_PVoid preserved)
{
    CModule *pModule;
    Aura_Bool ret = AURA_TRUE;

    //tachLock
    ForEachDLinkNode(CModule *, pModule, pModuleList) {
        pModule->DetachThread(preserved);
    }

    return ret;
}

#endif
